package example.demo.scope;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
import sun.misc.Unsafe;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CountDownLatch;

@Slf4j
public class XiaoScope implements Scope {
    //借助cas保证线程安全问题，对读操作不做线程安全问题，有脏读。。。
    private final Map<String, Object> xiaoObjectMap = new HashMap<>(4);

    private static final Unsafe unsafe;
    private static final long valueOffset;
    private volatile int value;

    static {
        try {
            Class<Unsafe> unsafeClass = Unsafe.class;
            Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
            theUnsafe.setAccessible(true);
            unsafe = (Unsafe) theUnsafe.get(null);
            valueOffset = unsafe.objectFieldOffset
                    (XiaoScope.class.getDeclaredField("value"));
        } catch (Exception ex) {
            throw new Error(ex);
        }
    }

    private void addObjToXiaoObjectMap(String beanName, Object obj) {
        int i = 0;
        try {
            while (!unsafe.compareAndSwapInt(this, valueOffset, 0, 1)) {//lock
                if (((++i) & 31) == 0) {
                    Thread.yield();
                    i = 0;
                }
            }
            xiaoObjectMap.put(beanName, obj);
        } finally {
            //unlock
            value = 0;
        }
    }


    private Object removeObjFromXiaoObjectMap(String beanName) {
        int i = 0;
        Object o;
        try {
            while (!unsafe.compareAndSwapInt(this, valueOffset, 0, 1)) {//lock
                if (((++i) & 31) == 0) {
                    Thread.yield();
                    i = 0;
                }
            }
            o = xiaoObjectMap.get(beanName);
        } finally {
            //unlock
            value = 0;
        }
        return o;
    }


//    /**
//     * test看我cas是否使用succeed
//     */
//    static final int thread_count = 1000;
//    static final CountDownLatch countDownLatch = new CountDownLatch(thread_count);
//    static final CountDownLatch mainDownLatch = new CountDownLatch(thread_count);
//
//    public static void main(String[] args) throws Exception {
//        XiaoScope xiaoScope = new XiaoScope();
//        for (int i = 0; i < thread_count; i++) {
//            new Thread(() -> {
//                log.info(Thread.currentThread().getName() + "===========start");
//                try {
//                    countDownLatch.await();
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
////                xiaoScope.xiaoObjectMap.put(Thread.currentThread().getName(), Thread.currentThread().getName());//偶尔有999
//                xiaoScope.addObjToXiaoObjectMap(Thread.currentThread().getName(), Thread.currentThread().getName());//都是1000
//                mainDownLatch.countDown();
//                log.info(Thread.currentThread().getName() + "===========end");
//            }).start();
//            countDownLatch.countDown();
//        }
//        mainDownLatch.await();
//        Iterator<Map.Entry<String, Object>> iterator = xiaoScope.xiaoObjectMap.entrySet().iterator();
//        while (iterator.hasNext()) {
//            Map.Entry<String, Object> next = iterator.next();
//            log.info(next.getKey() + "=====" + next.getValue());
//        }
//        System.out.println(xiaoScope.xiaoObjectMap.size());
//    }


    @Override
    public Object get(String name, ObjectFactory<?> objectFactory) {
        Object o = xiaoObjectMap.get(name);
        if (null != o) {
            return o;
        }
        //获取实例对象
        o = objectFactory.getObject();
        this.addObjToXiaoObjectMap(name, o);
        return o;
    }

    @Override
    public Object remove(String name) {
        return this.removeObjFromXiaoObjectMap(name);
    }

    @Override
    public void registerDestructionCallback(String name, Runnable callback) {

    }

    @Override
    public Object resolveContextualObject(String key) {
        return null;
    }

    @Override
    public String getConversationId() {
        return null;
    }
}
